/*
 * Date         Author        Note
 * 2015.06.26   wzwz          解密STM32升级文件
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <mh/common.h>
#include <mh/dsp2812.h>
#include <mh/mh_knit.h>
#include "func.h"
#include "md5.h"
#include "des.h"


/*
 *
 */
static ushort CheckSum(unsigned short *data, unsigned short size)
{
    long sum = 0;
    unsigned short d = 0;
    uchar *p = (uchar*)data;
    int i = 0;

    while (size > 1)
    {
        d = p[i] | (p[i + 1] << 8);
        sum += d;
        i += 2;
        size -= sizeof(unsigned short);
    }

    if (size > 0)
    {
        sum += p[i];
    }

    while (sum >> 16)
        sum = (sum >> 16) + (sum & 0xFFFF);

    sum = ~sum & 0xFFFF;
    if (!sum)
        sum = 0xFFFF;

    return (unsigned short)sum;
}
/*
 * decrypt update file
 */
int ReadUpdateFile(const char *filename, char prog_out[1024*128], unsigned int usage)
{
    int ret = ENC_EOK;
    FILE *fdsp;
    char *buf_fle;
    struct dsp_program *program;
    struct program_head *phead;
    int size_fle;
    int length;
    unsigned i;
    const char key[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};

    if (prog_out == NULL)
    {
        return ENC_ENOMEM;
    }

    fdsp = fopen(filename, "rb");
    if(!fdsp)
    {
        return ENC_EOFF;
    }

    fseek(fdsp, 0, SEEK_END);
    size_fle = ftell(fdsp);
    fseek(fdsp, 0, SEEK_SET);

    buf_fle = calloc(1, size_fle);
    if (!buf_fle)
    {
        fclose(fdsp);
        return ENC_ENOMEM;
    }

    length = fread(buf_fle, sizeof(char), size_fle, fdsp);
    if (length != size_fle)
    {
        ret = ENC_EFRD;
        goto _exit;
    }

    if(1)
    {
        MD5_CTX context;
        char *in, *out;
        char md5[16];
        subkey_t subkey;

        phead = (struct program_head*)buf_fle;

        if (phead->head.magic != FILEHEAD_MAGIC)
        {
            ret = ENC_EINVH;
            goto _exit;
        }
        if (CheckSum((unsigned short*)&phead->head, sizeof(filehead_t)) != 0xFFFF)
        {
            ret = ENC_ECHK;
            goto _exit;
        }
        if((phead->head.check_alg != CHK_ALG_MD5) || (phead->head.encrypt_alg != ENC_ALG_DES))
        {
            ret = ENC_EINVH;
            goto _exit;
        }
        if(usage && (phead->head.usage != usage))
        {
            ret = ENC_EUSAGE;
            goto _exit;
        }

        program = (struct dsp_program*)prog_out;
        out = (char*)program;
        in = (char*)buf_fle + phead->head.data_offset;

        DES_MakeSubKey(key, subkey);
        for (i = 0; i < phead->dsize; i += 8)
        {
            DES_DoWork(&in[i], &out[i], subkey, DECRYPT);
        }

        MD5Init(&context);
        MD5Update(&context, (unsigned short*)program, phead->dsize);
        MD5Final(&context, md5);

        if (memcmp(md5, phead->md5, 16) != 0)
        {
            ret = ENC_ECHK;
            goto _exit;
        }
    }

    for (i = 0; i <= program->section_cur; i ++)
    {
        program->sec[i].data = (unsigned short*)((char*)program->bin + program->sec[i].offset);
    }

_exit:
    fclose(fdsp);
    free(buf_fle);

    return ret;
}
/*
 *
 */
int ReadArmUpdateFile(const char *fullpath, char prog_out[1024*128], unsigned int usage)
{
    int ret = ENC_EOK;
    FILE *f_arm;
    char *buf_fle;
    int size_fle;
    int length;
    unsigned i;
    struct program_head *phead;

    const char key[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};

    if (prog_out == NULL)
    {
        printf("param: prog_out is NULL");

        return ENC_ENOMEM;
    }

    /* �Զ����Ʒ�ʽ�����ļ� */
    if ((f_arm = fopen(fullpath, "rb")) == NULL)
    {
        printf("can't open file: %s\n", fullpath);
        return ENC_EOFF;
    }

    fseek(f_arm, 0, SEEK_END);
    size_fle = ftell(f_arm);
    fseek(f_arm, 0, SEEK_SET);

    if ((buf_fle = (char*)malloc(size_fle)) == NULL)
    {
        printf("alloc buffer fail\n");

        fclose(f_arm);

        return ENC_ENOMEM;
    }

    length = fread(buf_fle, sizeof(char), size_fle, f_arm);
    if (length != size_fle)
    {
        printf("read file fail\n");

        ret = ENC_EFRD;
        goto _exit;
    }

    /**** ��ԭ��� ****/
    MD5_CTX context;
    char md5[16];
    subkey_t subkey;
    char *program;
    char *in;
    phead = (struct program_head*)buf_fle;

    if (phead->head.magic != FILEHEAD_MAGIC)
    {
        printf("involid file head\n");
        ret = ENC_EINVH;
        goto _exit;
    }
    if (CheckSum((unsigned short*)&phead->head, sizeof(filehead_t)) != 0xFFFF)
    {
        printf("file head check fail\n");
        ret = ENC_ECHK;
        goto _exit;
    }
    if ((phead->head.check_alg != CHK_ALG_MD5) || (phead->head.encrypt_alg != ENC_ALG_DES))
    {
        printf("can not surport this alg\n");
        ret = ENC_EINVH;
        goto _exit;
    }
    if (usage && (phead->head.usage != usage))
    {
        ret = ENC_EUSAGE;
        goto _exit;
    }

    program = (char *)prog_out;
    in = (char*)buf_fle + phead->head.data_offset;
    DES_MakeSubKey(key, subkey);
    for (i = 0; i < phead->dsize; i += 8)
    {
        DES_DoWork(&in[i], &program[i], subkey, DECRYPT);
    }
    goto _exit;
    printf("MD5...\n");
    MD5Init(&context);
    MD5Update(&context, (unsigned short*)program,phead->dsize);
    MD5Final(&context, md5);
    if (memcmp(md5,phead->md5, 16) != 0)
    {
        printf("file check fail\n");
        ret = ENC_ECHK;
        goto _exit;
    }

_exit:
    fclose(f_arm);
    free(buf_fle);
    return ret;
}
